package org.activemonitoring.harvesters;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import org.apache.log4j.Logger;

public class CyclicLatch {

    private static final Logger logger = Logger.getLogger(CyclicLatch.class);

    private CountDownLatch latch;
    private final Lock lock = new ReentrantLock();

    private int elementCount;
    private long timeout;
    private TimeUnit timeUnit;

    public CyclicLatch(int initialCount, long initialTimeout, TimeUnit initialTimeUnit) {
        this.elementCount = initialCount;
        this.timeout = initialTimeout;
        this.timeUnit = initialTimeUnit;
        this.latch = createCountDownLatch();
    }

    protected CountDownLatch createCountDownLatch() {
        if (logger.isDebugEnabled()) {
            logger.debug("Creating new CountDownLatch for " + this.elementCount + " elements");
        }
        return new CountDownLatch(this.elementCount);
    }

    public void countDown() {
        logger.debug("latch.countDown()");
        try {
            this.lock.lock();
            this.latch.countDown();
        } finally {
            this.lock.unlock();
        }
    }

    /**
     * Await the opening of the Latch
     * 
     * @return false if the opening has been done on Timeout, true if the opening has been done on overflow.
     * @throws InterruptedException
     */
    public boolean await() throws InterruptedException {
        if (logger.isInfoEnabled()) {
            logger.info("Waiting for latch of " + this.elementCount + " elements or " + this.timeout + " "
                    + this.timeUnit.name() + " timeout");
        }
        boolean result = this.latch.await(this.timeout, this.timeUnit);
        if (logger.isDebugEnabled()) {
            logger.debug("latch.await(): " + result);
        }
        /* Reset Latch, on timeout & on overflow */
        try {
            this.lock.lock();
            if (logger.isInfoEnabled()) {
                logger.info("Reseting latch...." + this.latch);
            }
            this.latch = createCountDownLatch();
        } finally {
            this.lock.unlock();
        }
        return result;

    }

    public void setElementCount(int elementCount) {
        this.elementCount = elementCount;
    }

    public void setTimeout(long timeout, TimeUnit timeUnit) {
        this.timeout = timeout;
        this.timeUnit = timeUnit;
    }

    @Override
    public String toString() {
        return this.latch.toString() + "[timeOut = " + this.timeout + ' ' + this.timeUnit.name() + "]";
    }
}
